import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import cloneDeep from 'lodash.clonedeep'
import { nanoid } from 'nanoid'
import { arrayMove } from '@dnd-kit/sortable'
import { getNextSelectedId, insertNewComponent } from './util'
import { ComponentPropsType } from '../../components/QuestionComponents'

export type ComponentInfoType = {
    fe_id: string
    type: string
    title: string
    isHidden?: boolean
    isLocked?: boolean
    props: ComponentPropsType
}

export type ComponentsStateType = {
    selectedId: string
    componentsList: Array<ComponentInfoType>
    copiedComponent?: ComponentInfoType | null
}

const INIT_STATE: ComponentsStateType = {
    selectedId: '',
    componentsList: [],
    copiedComponent: null,
}

export const componentSlice = createSlice({
    name: 'components ',
    initialState: INIT_STATE,
    reducers: {
        // 重置所有组件[ 接口返回组件列表之后，更新store中的组件列表数据 ]
        resetComponents: (
            state: ComponentsStateType, // 当前的类型
            action: PayloadAction<ComponentsStateType>
        ) => {
            return action.payload
        },
        /**
         * 修改选中的selectedId
         * --------- immer 版本更新 ----------
         * Redux Toolkit允许我们在reducers中编写“突变”逻辑。[ 用immer改善react state不可变的写法 ]
         * 它实际上并没有改变状态，因为它使用了Immer库，
         * 它检测到“草稿状态”的变化并产生全新的
         * 基于这些更改的不可变状态
         */
        changeSelectedId: (state: ComponentsStateType, action: PayloadAction<string>) => {
            state.selectedId = action.payload
        },

        /**
         * 添加组件到画布上
         * 添加逻辑是：如果当前没有选中的项，那么添加到最后一个
         * 如果当前有选中的项，那么添加到当前选中项的下一个
         */
        addComponentToCanvas: (
            state: ComponentsStateType,
            action: PayloadAction<ComponentInfoType>
        ) => {
            const newCmp = action.payload
            insertNewComponent(state, newCmp)
        },

        /**
         * 修改componentList的props属性，将右侧最新变更的属性同步到画布上来
         */
        changeComponentProps: (
            state: ComponentsStateType,
            action: PayloadAction<{ fe_id: string; newProps: ComponentPropsType }>
        ) => {
            const { fe_id, newProps } = action.payload
            // 根据fe_id找到当前要修改的组件
            const currentComponent = state.componentsList.find(c => c.fe_id === fe_id)
            // 更新组件属性
            if (currentComponent) {
                currentComponent.props = {
                    ...currentComponent.props,
                    ...newProps,
                }
            }
        },
        /**
         * 删除选中的组件
         * 从store中就能找到当前选中的组件是哪个
         */
        removeComponent: (state: ComponentsStateType) => {
            const { selectedId: removeId, componentsList = [] } = state
            // 重新计算selectedId
            state.selectedId = getNextSelectedId(removeId, componentsList)
            const index = componentsList.findIndex(c => c.fe_id === removeId)
            componentsList.splice(index, 1)
        },

        /**
         * 隐藏当前选中的组件
         */
        invisibleComponent: (
            state: ComponentsStateType,
            action: PayloadAction<{ fe_id: string; isHidden: boolean }>
        ) => {
            const { componentsList = [] } = state
            const { isHidden, fe_id } = action.payload

            let newSelectId = ''
            if (isHidden) {
                // 要隐藏
                newSelectId = getNextSelectedId(fe_id, componentsList)
            } else {
                // 要显示
                newSelectId = fe_id
            }
            state.selectedId = newSelectId

            const currComponent = componentsList.find(c => c.fe_id === fe_id)
            if (currComponent) {
                currComponent.isHidden = isHidden
            }
        },

        /**
         * @param state 切换组件的锁定状态
         * @param action 更新锁定状态的组件id
         */
        toggleComponentLocked: (
            state: ComponentsStateType,
            action: PayloadAction<{ fe_id: string }>
        ) => {
            const { componentsList = [] } = state
            const { fe_id } = action.payload
            const currComponent = componentsList.find(c => c.fe_id === fe_id)
            if (currComponent) {
                currComponent.isLocked = !currComponent.isLocked
            }
        },
        /**
         * 复制当前选中的组件
         */
        copySelectedComponent: (state: ComponentsStateType) => {
            const { selectedId, componentsList } = state
            const currSelectedComponent = componentsList.find(c => c.fe_id === selectedId)
            if (currSelectedComponent == null) return
            // 组件用深拷贝
            state.copiedComponent = cloneDeep(currSelectedComponent)
        },

        /**
         * 粘贴选中的组件
         * @param state
         * @returns
         */
        pasteSelectedComponent: (state: ComponentsStateType) => {
            const { copiedComponent } = state
            if (copiedComponent == null) return
            // 插入复制的组件，要把需要复制粘贴组件的fe_id进行修改。fe_id不能重复
            copiedComponent.fe_id = nanoid()
            insertNewComponent(state, copiedComponent)
        },
        /**
         * 选中上一个
         */
        selectPrevComponent: (state: ComponentsStateType) => {
            const { selectedId, componentsList } = state
            const selectedIndex = componentsList.findIndex(c => c.fe_id === selectedId)
            if (selectedIndex < 0) return // 未选中
            if (selectedIndex <= 0) return // 已经选中了第一个，无法再向上选中
            state.selectedId = componentsList[selectedIndex - 1].fe_id
        },
        /**
         * 选中下一个
         */
        selectNextComponent: (state: ComponentsStateType) => {
            const { selectedId, componentsList } = state
            const selectedIndex = componentsList.findIndex(c => c.fe_id === selectedId)
            if (selectedIndex < 0) return // 未选中
            if (selectedIndex + 1 == componentsList.length) return // 已经选中最后一个，无法再向下选中
            state.selectedId = componentsList[selectedIndex + 1].fe_id
        },
        /**
         * 修改组件标题
         */ changeComponentTitle: (
            state: ComponentsStateType,
            action: PayloadAction<{ fe_id: string; title: string }>
        ) => {
            const { fe_id, title } = action.payload
            const { componentsList } = state
            const currComponent = componentsList.find(c => c.fe_id === fe_id)
            if (currComponent) currComponent.title = title
        },
        /**
         * 移动组件位置
         */
        moveComponent: (
            state: ComponentsStateType,
            action: PayloadAction<{ oldIndex: number; newIndex: number }>
        ) => {
            const { componentsList: currComponentList } = state
            const { oldIndex, newIndex } = action.payload
            state.componentsList = arrayMove(currComponentList, oldIndex, newIndex)
        },
    },
})

export const {
    resetComponents,
    changeSelectedId,
    addComponentToCanvas,
    changeComponentProps,
    removeComponent,
    invisibleComponent,
    toggleComponentLocked,
    copySelectedComponent,
    pasteSelectedComponent,
    selectPrevComponent,
    selectNextComponent,
    changeComponentTitle,
    moveComponent,
} = componentSlice.actions
export default componentSlice.reducer
